The Java programming language is an object-oriented high level programming language developed by Sun Microsystems Inc. and designed to be portable enough to be executed on a wide range of computers ranging from small personal computers up to supercomputers. Computer programs written in Java (and other languages) may be compiled into virtual machine instructions for execution by a Java Virtual Machine (JVM). In general, the Java virtual machine is an interpreter that decodes and executes the virtual machine instructions.
The virtual machine instructions for the Java virtual machine are bytecodes, meaning they include one or more bytes. The bytecodes are stored in a particular file format called a “class file.” In addition to the bytecodes, the class file includes a symbol table as well as other ancillary information.
A computer program embodied as Java bytecodes in one or more class files is platform independent. The computer program may be executed, unmodified, on any computer that is able to run an implementation of the Java virtual machine. The Java virtual machine is a software emulator of a “generic” computer, which is a major factor in allowing computer programs for the Java virtual machine to be platform independent.
The Java virtual machine is commonly implemented as a software interpreter. Conventional interpreters decode and execute the virtual machine instructions of an interpreted program one instruction at a time during execution. Compilers, on the other hand, transform virtual machine instructions into native machine instructions prior to execution so that decoding is not performed on virtual machine instructions during execution. Because conventional interpreters repeatedly decode each instruction before it is executed each time the instruction is encountered, execution of interpreted programs is typically quite slower than compiled programs since the native machine instructions of compiled programs can be executed on the native machine or computer system directly.
As a software interpreter must be executing in order to decode and execute an interpreted program, the software interpreter consumes resources (e.g., memory) that will no longer be available to the interpreted program. This is in contrast to compiled programs that execute as native machine instructions so they may be directly executed on the target computer and therefore generally require fewer resources than interpreted programs.
Accordingly, there is a need for new techniques for increasing the execution speed of computer programs that are being interpreted. Additionally, there is a need to provide interpreters that are efficient in terms of the resources they require.
Many embedded devices are becoming Java enabled due to the platform independent and ubiquitous nature of the Java language and development of Java Virtual Machine implementations. Many low cost embedded systems desire to implement the Java processing function as a JVM to avoid the hardware overhead of a dedicated Java processor or a hardware accelerator (for example, a separate co-processor). A straightforward approach to implementing the JVM and executing a Java program is through the use of a software interpreter.
The interpreter implements a software version of a processor in the sense that it performs the standard functions of instruction fetch, decode, and execute of the interpreted instruction stream. The interpreter typically is implemented as a program loop that iterates the fetch, decode, and execute steps.
As Java instructions (bytecodes) are fetched, the interpreter maintains a virtual program counter (the Java PC), and the decoding of each interpreted Java instruction is performed by a large case statement (switch statement) with clauses corresponding to each binary encoding of the bytecode. Code within each clause performs the actual operation defined by the bytecode.
There is overhead associated with processing each bytecode since the bytecode must be fetched and decoded. The overhead associated with the fetch and decode portions of the sequence can be a large percentage of overall execution time, since many Java instructions (bytecodes) perform simple operations.
Since the Java environment supports the notion of multiple threads of execution, an additional function of the interpreter loop is to maintain a counter that is used in determining when a thread (or task) switch should occur.
In some implementations of a Java Virtual Machine, the threadswitch determination is based on the number of Java bytecodes executed. By keeping a counter value which is modified each time the interpreter loop executes, and comparing it to a predetermined value, the interpreter implements the thread switch logic as well. Maintaining the counter and determining when to threadswitch also creates significant overhead. This overhead significantly limits the operating efficiency of the software interpreter.